#include "systemc.h"

SC_MODULE(SimpleAPB)
{
	sc_in_clk PCLK;
	sc_in<bool> PRESETn;
	sc_in<int> PADDR; // 32bit wide
	sc_in<bool> PSELx;
	sc_in<bool> PENABLE;
	sc_in<bool> PWRITE;
	sc_in<int> PWDATA; // 32bit write data
	sc_out<bool> PREADY;
	sc_out<int> PRDATA;
	sc_out<bool> PSLVERR; // tie low
	int mem[1024];
	bool ready;
	SC_CTOR(SimpleAPB)
	{
		SC_METHOD(trans);
		sensitive << PCLK.pos();
		sensitive << PRESETn.neg();
		for (int i = 0; i < 1024; i++)
		{
			mem[i] = i;
		}
		ready = true;
	}

	void trans()
	{
		if (PRESETn.read() == false)
		{
			ready = true;
			PREADY.write(1);
			return;
		}
		if (PSELx.read() == true && PENABLE.read() == true && ready)
		{
			int addr = (PADDR.read() >> 2) % 1024;

			// cout << "trans: " << addr << "---" << mem[addr] << endl;
			if (PWRITE.read() == false)
				PRDATA.write(mem[addr]);
			else
				mem[addr] = PWDATA.read();
			ready = false;
		}
		else
		{
			ready = true;
		}
		PREADY.write(ready);

		// if (PRESETn.read() == true && PWRITE.read() == false)
		// 	cout << __LINE__ << " : " << __func__ << ": " << sc_time_stamp() << endl;
	}
};

SC_MODULE(IFU)
{
	sc_in_clk PCLK;
	sc_in<bool> PRESETn;
	// APB load data interface
	sc_out<int> PADDR; // 32bit wide
	sc_out<bool> PSELx;
	sc_out<bool> PENABLE;
	sc_out<bool> PWRITE;
	sc_out<int> PWDATA; // 32bit write data
	sc_in<bool> PREADY;
	sc_in<int> PRDATA;
	sc_in<bool> PSLVERR; // tie low

	// to dec
	sc_out<int> ifu_opc;
	sc_out<int> ifu_ia;
	sc_out<bool> ifu_opc_vaild;
	// from dec
	sc_in<bool> dec_full;
	sc_in<int> dec_restart_ia;
	sc_in<bool> dec_restart;
	int ia;
	int opc;
	bool fetch_opc_full;
	bool fetch_opc_vaild;

	SC_CTOR(IFU)
	{
		SC_CTHREAD(fetch_inst, PCLK.pos());
		SC_METHOD(pass_inst);
		sensitive << PCLK.pos();
		sensitive << PRESETn.neg();

		fetch_opc_full = false;
		fetch_opc_vaild = false;
		ia = 0x0; // reset
	}
	void fetch_inst()
	{
		while (true)
		{
			wait();
			if (PREADY.read() == true && fetch_opc_full == false)
			{
				PSELx.write(1);
				wait();
				PADDR.write(ia);
				PWRITE.write(0);
				wait();
				PENABLE.write(1);

				while (PREADY.read() == true)
				{
					// cout << "not ready...." << endl;
					wait();
				}

				opc = PRDATA.read();
				// cout << "fetch: " << ia << "--" << opc << endl;
				fetch_opc_vaild = true;
				PSELx.write(0);
				PENABLE.write(0);
			}
		}
	}

	void pass_inst()
	{
		if (PRESETn.read() == false)
		{
			ia = 0x0;
			fetch_opc_vaild = false;
			fetch_opc_full = false;
			ifu_opc_vaild.write(0);
			return;
		}

		if (dec_restart.read())
		{
			ia = dec_restart_ia.read();
			fetch_opc_vaild = false;
			fetch_opc_full = false;
		}

		if (!dec_full.read() && fetch_opc_vaild)
		{
			// cout << "pass inst: " << ia << "---" << opc << endl;
			ifu_opc.write(opc);
			ifu_ia.write(ia);
			ifu_opc_vaild.write(1);
			fetch_opc_vaild = false;
			fetch_opc_full = false;
			ia += 4;
		}
		else
		{
			ifu_opc_vaild.write(0);
		}
	}
};

SC_MODULE(DEC)
{
	sc_in_clk PCLK;
	sc_in<bool> PRESETn;

	// to dec
	sc_in<int> ifu_opc;
	sc_in<int> ifu_ia;
	sc_in<bool> ifu_opc_vaild;
	// from dec
	sc_out<bool> dec_full;
	sc_out<int> dec_restart_ia;
	sc_out<bool> dec_restart;

	SC_CTOR(DEC)
	{
		SC_METHOD(decode);
		sensitive << PCLK.pos();
		sensitive << PRESETn.neg();
	}

	void _void_decode_opc(int opcode)
	{
		cout << hex << "decode org opc: " << opcode << endl;
	}

	void decode()
	{
		if (PRESETn.read() == false)
		{
			dec_full.write(0);
			dec_restart_ia.write(0);
			dec_restart.write(1);
			return;
		}

		if (ifu_opc_vaild.read() == true)
		{
			_void_decode_opc(ifu_opc.read());
		}

		// 检测是否分支正确,目前默认正确
		dec_restart.write(0);
	}

	void issue()
	{
	}
};

int sc_main(int argc, char *argv[])
{
	sc_signal<bool> PCLK;
	sc_signal<bool> PRESETn;
	sc_signal<int> PADDR; // 32bit wide
	sc_signal<bool> PSELx;
	sc_signal<bool> PENABLE;
	sc_signal<bool> PWRITE;
	sc_signal<int> PWDATA; // 32bit write data
	sc_signal<bool> PREADY;
	sc_signal<int> PRDATA;
	sc_signal<bool> PSLVERR; // tie low

	// to dec
	sc_signal<int> ifu_opc;
	sc_signal<int> ifu_ia;
	sc_signal<bool> ifu_opc_vaild;
	// from dec
	sc_signal<bool> dec_full;
	sc_signal<int> dec_restart_ia;
	sc_signal<bool> dec_restart;

	SimpleAPB uAPB("uapb0");
	uAPB.PCLK(PCLK);
	uAPB.PRESETn(PRESETn);
	uAPB.PADDR(PADDR);
	uAPB.PSELx(PSELx);
	uAPB.PENABLE(PENABLE);
	uAPB.PWRITE(PWRITE);
	uAPB.PWDATA(PWDATA);
	uAPB.PREADY(PREADY);
	uAPB.PRDATA(PRDATA);
	uAPB.PSLVERR(PSLVERR);

	IFU uIFU("uifu");
	uIFU.PCLK(PCLK);
	uIFU.PRESETn(PRESETn);
	uIFU.PADDR(PADDR);
	uIFU.PSELx(PSELx);
	uIFU.PENABLE(PENABLE);
	uIFU.PWRITE(PWRITE);
	uIFU.PWDATA(PWDATA);
	uIFU.PREADY(PREADY);
	uIFU.PRDATA(PRDATA);
	uIFU.PSLVERR(PSLVERR);
	uIFU.ifu_opc(ifu_opc);
	uIFU.ifu_ia(ifu_ia);
	uIFU.ifu_opc_vaild(ifu_opc_vaild);
	uIFU.dec_full(dec_full);
	uIFU.dec_restart_ia(dec_restart_ia);
	uIFU.dec_restart(dec_restart);

	DEC uDEC("udec");
	uDEC.PCLK(PCLK);
	uDEC.PRESETn(PRESETn);
	uDEC.ifu_opc(ifu_opc);
	uDEC.ifu_ia(ifu_ia);
	uDEC.ifu_opc_vaild(ifu_opc_vaild);
	uDEC.dec_full(dec_full);
	uDEC.dec_restart_ia(dec_restart_ia);
	uDEC.dec_restart(dec_restart);

	// initlaize
	// sc_start(SC_ZERO_TIME);
	PRESETn.write(1);
	sc_start(1, SC_NS);
	PRESETn.write(0);
	sc_start(1, SC_NS);
	PRESETn.write(1);
	for (int i = 0; i < 20; i++)
	// for (;;)
	{
		PCLK.write(0);
		sc_start(20, SC_NS);
		PCLK.write(1);
		sc_start(20, SC_NS);
		// cout << "==" << endl;
	}
	return 0;
}
